<?php
/**
 * +--------------------------------------------------------------
 * | eLuoPHP
 * +--------------------------------------------------------------
 * | Copyright (c) 2013-2019 http://qxwoo.cn All rights reserved.
 * +--------------------------------------------------------------
 * | y139w <y139w@163.com>
 * +--------------------------------------------------------------
 * | 2019/07/21 19:14:56
 * +--------------------------------------------------------------
 * | Manager.php 文件管理器类
 * +--------------------------------------------------------------
 */


namespace luo;


class Manager
{

    /**
     * @var string
     */
    private $phpPath = '';

    /**
     * @var string
     */
    private $phpUrl = '';

    /**
     * @var string
     */
    private $rootPath = '';

    /**
     * @var string
     */
    private $rootUrl = '';

    /**
     * @var array
     */
    private $extArray = ['gif', 'jpg', 'jpeg', 'png', 'bmp'];

    /**
     * Manager constructor.
     */
    public function __construct()
    {

        // 网站根目录
        $document_root = $_SERVER['DOCUMENT_ROOT'];
        // 入口文件目录
        $script_filename = dirname($_SERVER['SCRIPT_FILENAME']);
        $this->phpPath = $script_filename;
        // 网站相对路径
        $root = str_replace($document_root, '', $script_filename);
        $root = ($root === '/') ? '/' : $root . '/';
        $this->phpUrl = $root;

    }

    /**
     * @param $dir
     * @param $path
     * @param $order
     * @param string $root
     * @return array
     */
    public function files($dir, $path, $order, $root = ''){

        if(!empty($root)){
            $this->root($root);
        }
        $directory = $this->directory($dir);
        if($directory){
            $paths = $this->paths($path);
            if(!$this->allow($paths['currentPath'])){
                $result = ['code' => 0, 'error' => '不允许访问'];
            }else{
                if($this->slash($paths['currentPath'])){
                    if(!$this->exist($paths['currentPath'])){
                        $result = ['code' => 0, 'error' => '目录不存在'];
                    }else{
                        $fileList = $this->fileList($paths['currentPath'], $this->extArray);
                        $fileList = $this->sort($fileList, $order);
                        $result = [
                            'code'  => 200,
                            'error' => '成功',
                            'data'  => $paths
                        ];
                        $result['data']['fileCount'] = count($fileList);
                        $result['data']['fileList'] = $fileList;
                    }
                }else{
                    $result = ['code' => 0, 'error' => '参数无效'];
                }
            }
        }else{
            $result = ['code' => 0, 'error' => '无效目录名'];
        }
        return $result;

    }

    /**
     * @param $root
     * @return $this
     */
    public function root($root){

        $this->rootPath = $this->phpPath . '/' . $root;
        $this->rootUrl = $this->phpUrl . $root;
        return $this;

    }

    /**
     * 根据参数处理要使用到的路径和URL
     * @param $path
     * @return array
     */
    private function paths($path){

        //根据path参数，设置各路径和URL
        if (empty($path)) {
            $paths = [
                'currentPath' => realpath($this->rootPath) . '/',
                'currentUrl'  => $this->rootUrl,
                'currentDirPath' => '',
                'moveUpDirPath'  => ''
            ];
        } else {
            $paths = [
                'currentPath' => realpath($this->rootPath) . '/' . $path,
                'currentUrl'  => $this->rootUrl . $path,
                'currentDirPath' => $path,
                'moveUpDirPath'  => preg_replace('/(.*?)[^\/]+\/$/', '$1', $path)
            ];
        }
        return $paths;

    }

    /**
     * 验证目录名是否正确，并处理目录路径
     * 如果路径不存在，就创建目录路径
     * @param $dir
     * @return bool
     */
    private function directory($dir){

        $directory = true;
        //目录名
        $dirName = empty($dir) ? '' : trim($dir);
        if (!in_array($dirName, array('', 'image', 'flash', 'media', 'file'))) {
            return $directory;
        }
        // 路径处理
        if ($dirName !== '') {
            $this->rootPath .= $dirName . "/";
            $this->rootUrl .= $dirName . "/";
            // 路径是否存在，如果不存在就创建路径。
            if (!file_exists($this->rootPath)) {
                mkdir($this->rootPath);
            }
        }
        return $directory;

    }

    /**
     * 当前目录是否允许访问
     * @param $currentPath
     * @return bool
     */
    private function allow($currentPath){

        $allow = true;
        //不允许使用..移动到上一级目录
        if (preg_match('/\.\./', $currentPath)) {
            $allow = false;
        }
        return $allow;

    }

    /**
     * 验证最后一个字符是否是斜杠“/”
     * @param $currentPath
     * @return bool
     */
    private function slash($currentPath){

        $slash = true;
        //最后一个字符不是/
        if (!preg_match('/\/$/', $currentPath)) {
            $slash = false;
        }
        return $slash;

    }

    /**
     * 目录是否存在或是否是目录
     * @param $currentPath
     * @return bool
     */
    private function exist($currentPath){

        $exist = true;
        //目录不存在或不是目录
        if (!file_exists($currentPath) || !is_dir($currentPath)) {
            $exist = false;
        }
        return $exist;

    }

    /**
     * 获取当前目录中的所有文件或文件夹
     * @param $currentPath
     * @param $extArray
     * @return mixed
     */
    private function fileList($currentPath, $extArray){

        //遍历目录取得文件信息
        $fileList = [];
        if ($handle = opendir($currentPath)) {
            $i = 0;
            while (false !== ($filename = readdir($handle))) {
                if ($filename{0} == '.'){
                    continue;
                }
                $file = $currentPath . $filename;
                if (is_dir($file)) {
                    $fileArray = [
                        //是否文件夹
                        'is_dir' => true,
                        //文件夹是否包含文件
                        'has_file' => (count(scandir($file)) > 2),
                        //文件大小
                        'filesize' => 0,
                        //是否图片
                        'is_photo' => false,
                        //文件类别，用扩展名判断
                        'filetype' => ''
                    ];
                } else {
                    $fileExt = strtolower(pathinfo($file, PATHINFO_EXTENSION));
                    $fileArray = [
                        //是否文件夹
                        'is_dir' => false,
                        //文件夹是否包含文件
                        'has_file' => false,
                        //文件大小
                        'filesize' => filesize($file),
                        //目录路径
                        'dir_path' => '',
                        //是否图片
                        'is_photo' => in_array($fileExt, $extArray),
                        //文件类别，用扩展名判断
                        'filetype' => $fileExt
                    ];
                }
                //文件名，包含扩展名
                $fileArray['filename'] = $filename;
                //文件最后修改时间
                $fileArray['datetime'] = date('Y-m-d H:i:s', filemtime($file));
                $fileList[$i] = $fileArray;
                $i++;
            }
            closedir($handle);
        }
        return $fileList;

    }

    /**
     * 对文件列表数组数据进行排序
     * @param $fileList
     * @param $order
     * @return mixed
     */
    private function sort($fileList, $order){

        $order = empty($order) ? 'name' : strtolower($order);
        //排序函数
        $cmp_func = function ($a, $b) use ($order) {
            if ($a['is_dir'] && !$b['is_dir']) {
                return -1;
            } else if (!$a['is_dir'] && $b['is_dir']) {
                return 1;
            } else {
                if ($order == 'size') {
                    if ($a['filesize'] > $b['filesize']) {
                        return 1;
                    } else if ($a['filesize'] < $b['filesize']) {
                        return -1;
                    } else {
                        return 0;
                    }
                } else if ($order == 'type') {
                    return strcmp($a['filetype'], $b['filetype']);
                } else {
                    return strcmp($a['filename'], $b['filename']);
                }
            }
        };
        usort($fileList, $cmp_func);
        return $fileList;

    }

}